﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WhittleList.Tests.Mocks;
using WhittleList.Controllers;
using System.Web.Mvc;
using WhittleList.Bll;
using WhittleList.Tests.Helpers;

namespace WhittleList.Tests.Controllers
{
    /// <summary>
    /// Summary description for ProjectControllerTests
    /// </summary>
    [TestClass]
    public class ProjectControllerTests
    {
        private List<FakeSite> siteData;
        private List<FakeProject> projectData;
        private FakeUserRepository userRepository;
        private FakeSiteRepository siteRepository;
        private FakeHttpContext httpContext;
        private ProjectController controller;
        private Random rnd;

        [TestInitialize]
        public void Init()
        {
            rnd = new Random();
            siteData = FakeSite.GetList(2);
            projectData = FakeProject.GetList(10, rnd);
            siteData[0].SetProjects(projectData.Take(8).ToList());
            siteData[1].SetProjects(projectData.Skip(8).ToList());

            siteRepository = new FakeSiteRepository();
            siteRepository.SetSites(siteData);
            siteRepository.SetProjects(projectData);

            userRepository = new FakeUserRepository();
            userRepository.Setup(x => x.GetByEmailAddress("test")).Returns(new FakeUser(1, "Test", "User", "test", "password").Object);

            httpContext = new FakeHttpContext();
            httpContext.Request.Setup(x => x.IsAuthenticated).Returns(true);
            httpContext.Identity.Setup(x => x.IsAuthenticated).Returns(true);
            httpContext.Identity.Setup(x => x.Name).Returns("test");

            controller = new ProjectController(userRepository.Object, siteRepository.Object);
            httpContext.AttachToControllerContext(controller);
        }

        [TestMethod]
        public void ProjectController_Index_ValidId_ReturnsCorrectView_Test()
        {
            var result = controller.Index(siteData[0].Object);
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));
            var viewResult = (ViewResult)result;
            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(List<Project>));
            var data = (List<Project>)viewResult.ViewData.Model;
            Assert.AreEqual(siteData[0].Object.Projects.Count, data.Count);
        }

        [TestMethod]
        public void ProjectController_Details_ValidId_ReturnsCorrectView_Test()
        {
            var result = controller.Details(siteData[0].Object, projectData[4].Object.Id);
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));
            var viewResult = (ViewResult)result;
            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(Project));
            var data = (Project)viewResult.ViewData.Model;
            Assert.AreEqual(projectData[4].Object, data);
        }

        [TestMethod]
        public void ProjectController_Edit_ValidId_ReturnsCorrectView_Test()
        {
            var result = controller.Edit(siteData[0].Object, projectData[4].Object.Id);
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));
            var viewResult = (ViewResult)result;
            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(Project));
            var data = (Project)viewResult.ViewData.Model;
            Assert.AreEqual(projectData[4].Object, data);
        }

        [TestMethod]
        public void ProjectController_Create_ValidData_ReturnsCorrectView_Test()
        {
            var result = controller.Create(siteData[0].Object, "New Name", "New Summary");
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
            var redirectResult = (RedirectToRouteResult)result;
            Assert.IsNotNull(redirectResult.RouteValues["ProjectId"]);

            siteRepository.Verify(x => x.Insert(Moq.It.IsAny<Project>()), Moq.Times.Once());
            siteRepository.Verify(x => x.SaveChanges(Moq.It.IsAny<User>()), Moq.Times.Once());
        }

        [TestMethod]
        public void ProjectController_Edit_ValidData_ReturnsCorrectView_Test()
        {
            var result = controller.Edit(siteData[0].Object, siteData[0].Object.Projects[1].Id, "New Name", "New Summary", siteData[0].Object.Projects[1].CID);
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
            var redirectResult = (RedirectToRouteResult)result;
            Assert.AreEqual(siteData[0].Object.Projects[1].Id, redirectResult.RouteValues["Id"]);

            siteRepository.Verify(x => x.Insert(Moq.It.IsAny<Project>()), Moq.Times.Never());
            siteRepository.Verify(x => x.SaveChanges(Moq.It.IsAny<User>()), Moq.Times.Once());
        }

        [TestMethod]
        public void ProjectController_Edit_ValidationErrors_ReturnsCorrectView_Test()
        {
            var validationExceptions = new RuleViolation[]{new RuleViolation("Name", "Name Error"), new RuleViolation("Summary", "Summary Error")};

            siteRepository.Setup(x => x.SaveChanges(Moq.It.IsAny<User>())).Throws(new WhittleList.Bll.Exceptions.ValidationException("Rule Violation Error", validationExceptions));

            var result = controller.Edit(siteData[0].Object, projectData[0].Object.Id, "New Name", "New Summary", projectData[0].Object.CID);
            
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));
            var viewResult = (ViewResult)result;

            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(Project));
            var project = (Project)viewResult.ViewData.Model;
            Assert.AreEqual("New Name", project.Name);
            Assert.AreEqual("New Summary", project.Summary);

            Assert.IsFalse(viewResult.ViewData.ModelState.IsValid);
            Assert.AreEqual(2, viewResult.ViewData.ModelState.Count);
            Assert.IsNotNull(viewResult.ViewData.ModelState["Name"]);
            Assert.IsNotNull(viewResult.ViewData.ModelState["Summary"]);

        }

        [TestMethod]
        public void ProjectController_Edit_DuplicateName_ReturnsCorrectView_Test()
        {
            siteRepository.Setup(x => x.SaveChanges(Moq.It.IsAny<User>())).Throws(new WhittleList.Bll.Exceptions.DuplicateNameException());

            var result = controller.Edit(siteData[0].Object, projectData[0].Object.Id, "New Name", "New Summary", projectData[0].Object.CID);

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(ViewResult));
            var viewResult = (ViewResult)result;

            Assert.IsNotNull(viewResult.ViewData.Model);
            Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(Project));
            var project = (Project)viewResult.ViewData.Model;
            Assert.AreEqual("New Name", project.Name);
            Assert.AreEqual("New Summary", project.Summary);

            Assert.IsFalse(viewResult.ViewData.ModelState.IsValid);
            Assert.AreEqual(1, viewResult.ViewData.ModelState.Count);
            Assert.IsNotNull(viewResult.ViewData.ModelState["Name"]);

        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void ProjectController_Edit_ConcurrencyError_ThrowsNotImplimentedException()
        {
            var cid = MockHelpers.CreateBinary(rnd);

            var result = controller.Edit(siteData[0].Object, projectData[0].Object.Id, "New Name", "New Summary", cid);

        }

    }
}
